package cn.bossfriday.im.common.service;

import cn.bossfriday.common.combo.Combo2;
import cn.bossfriday.common.exception.ServiceRuntimeException;
import cn.bossfriday.common.plugin.PluginResource;
import cn.bossfriday.im.common.db.UserInfoDao;
import cn.bossfriday.im.common.db.entity.UserInfo;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static cn.bossfriday.im.common.constant.ImConstant.*;

/**
 * UserInfoService
 *
 * @author chenx
 */
@Slf4j
@Service
public class UserInfoService {

    @PluginResource
    private UserInfoDao userInfoDao;

    /**
     * Key: Combo2<appId, userId>
     */
    private Cache<Combo2<Long, String>, Optional<UserInfo>> userInfoCache = Caffeine.newBuilder()
            .initialCapacity(CACHE_DEFAULT_INITIAL_CAPACITY)
            .maximumSize(CACHE_DEFAULT_MAXIMUM_SIZE)
            .expireAfterWrite(CACHE_EXPIRE_TWO_HOURS, TimeUnit.SECONDS)
            .build();

    /**
     * getById
     *
     * @param appId
     * @param userId
     * @return
     */
    public UserInfo getById(long appId, String userId) {
        try {
            Combo2<Long, String> key = this.getUserCacheKey(appId, userId);
            Optional<UserInfo> cached = this.userInfoCache.getIfPresent(key);
            if (Objects.nonNull(cached)) {
                return cached.orElse(null);
            }

            return this.userInfoCache.get(key, k -> Optional.ofNullable(this.userInfoDao.getById(k.getV1(), k.getV2()))).orElse(null);
        } catch (Exception ex) {
            log.error("UserInfoService.getById() error!", ex);
            throw new ServiceRuntimeException("UserInfoService.getById() error!");
        }
    }

    /**
     * register
     *
     * @param appId
     * @param userId
     */
    public void register(long appId, String userId, String userName) throws SQLException {
        if (Objects.nonNull(this.getById(appId, userId))) {
            return;
        }

        // register user
        userName = StringUtils.isEmpty(userName) ? userId : userName;
        UserInfo userInfo = UserInfo.builder()
                .appId(appId)
                .userId(userId)
                .userName(userName)
                .build();
        this.userInfoDao.add(userInfo);
        log.info("add user done, appId: {}, userId: {}, userName: {}", appId, userId, userName);

        // remove cache
        this.removeCache(appId, userId);
    }

    /**
     * removeCache
     */
    private void removeCache(long appId, String userId) {
        Combo2<Long, String> key = this.getUserCacheKey(appId, userId);
        this.userInfoCache.invalidate(key);
    }

    /**
     * getUserCacheKey
     */
    private Combo2<Long, String> getUserCacheKey(long appId, String userId) {
        return new Combo2<>(appId, userId);
    }
}
